﻿using System;
using System.Windows;
using JellystonePark.Model;
using System.Collections.ObjectModel;

namespace JellystonePark.JellystoneWPF.ViewModel
{
    public class JellystoneViewModel : ViewModelBase
    {
        #region Fields

        private JellystoneModel _model;

        #endregion

        #region Properties

        /// <summary>
        /// Új játék parancs lekérdezése.
        /// </summary>
        public DelegateCommand NewGameCommand { get; private set; }

        /// <summary>
        /// Pályacsomag betöltése parancs lekérdezése.
        /// </summary>
        public DelegateCommand LoadLevelsCommand { get; private set; }

        /// <summary>
        /// Kilépés parancs lekérdezése.
        /// </summary>
        public DelegateCommand ExitCommand { get; private set; }

        /// <summary>
        /// Szüneteltetés parancsa.
        /// </summary>
        public DelegateCommand ToggleCommand { get; private set; }

        /// <summary>
        /// Balra lépés parancsa.
        /// </summary>
        public DelegateCommand LeftCommand { get; private set; }

        /// <summary>
        /// Felfelé lépés parancsa.
        /// </summary>
        public DelegateCommand UpCommand { get; private set; }

        /// <summary>
        /// Jobbra lépés parancsa.
        /// </summary>
        public DelegateCommand RightCommand { get; private set; }

        /// <summary>
        /// Lefelé lépés parancsa.
        /// </summary>
        public DelegateCommand DownCommand { get; private set; }

        /// <summary>
        /// Adott pályán eltelt játékidő lekérdezése.
        /// </summary>
        public string ElapsedTime { get { return TimeSpan.FromSeconds(_model.GameTime).ToString("g"); } }

        /// <summary>
        /// Adott pályán megtett lépések lekérdezése.
        /// </summary>
        public int Steps { get { return _model.Steps; } }

        /// <summary>
        /// Adott pályán begyűjtött kosarak száma.
        /// </summary>
        public int BasketsFounded { get { return _model.BasketsFounded; } }

        /// <summary>
        /// Játéktábla oldalhosszának lekérdezése.
        /// </summary>
        public int SideLength { get { return _model.CurrentTable.SideLength; } }

        /// <summary>
        /// Mezők lekérdezése.
        /// </summary>
        public ObservableCollection<JellystoneField> Fields { get; set; }

        #endregion

        #region Events

        /// <summary>
        /// Új játék indításának eseménye.
        /// </summary>
        public event EventHandler NewGame;

        /// <summary>
        /// Pályacsomag betöltésének eseménye.
        /// </summary>
        public event EventHandler LoadLevels;

        /// <summary>
        /// Szüneteltetés/Folytatás eseménye.
        /// </summary>
        public event EventHandler Toggled;

        /// <summary>
        /// Felfelé lépés eseménye.
        /// </summary>
        public event EventHandler SteppedUp;

        /// <summary>
        /// Jobbra lépés eseménye.
        /// </summary>
        public event EventHandler SteppedRight;

        /// <summary>
        /// Lefelé lépés eseménye.
        /// </summary>
        public event EventHandler SteppedDown;

        /// <summary>
        /// Balra lépés eseménye.
        /// </summary>
        public event EventHandler SteppedLeft;

        #endregion

        #region Constructors

        public JellystoneViewModel(JellystoneModel model)
        {
            _model = model;
            _model.Advanced += new EventHandler<JellystoneResultEventArgs>(_model_Advanced);
            _model.FigureMoved += new EventHandler<JellystoneVectorEventArgs>(_model_FigureMoved);
            _model.GotBasket += new EventHandler<JellystoneResultEventArgs>(_model_GotBasket);
            _model.Stepped += new EventHandler<JellystoneResultEventArgs>(_model_Stepped);

            NewGameCommand = new DelegateCommand(param => { OnNewGame(); SetupTable(); });
            LoadLevelsCommand = new DelegateCommand(param => { OnLoadLevels(); SetupTable(); });
            ToggleCommand = new DelegateCommand(param => { OnToggled(); });
            LeftCommand = new DelegateCommand(param => { OnStepLeft(); });
            UpCommand = new DelegateCommand(param => { OnStepUp(); });
            RightCommand = new DelegateCommand(param => { OnStepRight(); });
            DownCommand = new DelegateCommand(param => { OnStepDown(); });

            Fields = new ObservableCollection<JellystoneField>();
            SetupTable();
        }

        #endregion

        #region Event methods

        private void OnNewGame()
        {
            if (null != NewGame)
                NewGame(this, EventArgs.Empty);
        }

        private void OnLoadLevels()
        {
            if (null != LoadLevels)
                LoadLevels(this, EventArgs.Empty);
        }

        private void OnToggled()
        {
            if (null != Toggled)
                Toggled(this, EventArgs.Empty);
        }

        private void OnStepDown()
        {
            if (null != SteppedDown)
                SteppedDown(this, EventArgs.Empty);
        }

        private void OnStepRight()
        {
            if (null != SteppedRight)
                SteppedRight(this, EventArgs.Empty);
        }

        private void OnStepUp()
        {
            if (null != SteppedUp)
                SteppedUp(this, EventArgs.Empty);
        }

        private void OnStepLeft()
        {
            if (null != SteppedLeft)
                SteppedLeft(this, EventArgs.Empty);
        }

        #endregion

        #region Model event handlers

        private void _model_Stepped(object sender, JellystoneResultEventArgs e)
        {
            Application.Current.Dispatcher.Invoke(new Action(() => OnPropertyChanged("Steps")));
        }

        private void _model_GotBasket(object sender, JellystoneResultEventArgs e)
        {
            Application.Current.Dispatcher.Invoke(new Action(() => OnPropertyChanged("BasketsFounded")));
        }

        private void _model_FigureMoved(object sender, JellystoneVectorEventArgs e)
        {
            SetField(Fields[SideLength*e.Y + e.X]);
            SetField(Fields[SideLength*e.YTo + e.XTo]);
        }

        private void _model_Advanced(object sender, JellystoneResultEventArgs e)
        {
            Application.Current.Dispatcher.Invoke(new Action(() => OnPropertyChanged("ElapsedTime")));
        }

        #endregion

        #region Public methods

        public void SetupTable()
        {
            Application.Current.Dispatcher.Invoke(new Action(() => Fields.Clear()));
            for (int i = 0; i < SideLength; ++i)
            {
                for (int j = 0; j < SideLength; ++j)
                {
                    Application.Current.Dispatcher.Invoke(new Action(() =>
                        Fields.Add(new JellystoneField
                        {
                            X = i,
                            Y = j,
                            Value = string.Empty
                        }
                    )));
                }
            }
            Application.Current.Dispatcher.Invoke(new Action(() => OnPropertyChanged("SideLength")));
            Application.Current.Dispatcher.Invoke(new Action(() => OnPropertyChanged("Fields")));
            Application.Current.Dispatcher.Invoke(new Action(() => OnPropertyChanged("ElapsedTime")));
            Application.Current.Dispatcher.Invoke(new Action(() => OnPropertyChanged("BasketsFounded")));
            foreach (JellystoneField field in Fields)
                Application.Current.Dispatcher.Invoke(new Action(() => SetField(field)));
        }

        #endregion

        #region Private methods

        private void SetField(JellystoneField field)
        {
            switch (_model.CurrentTable.GetValue(field.Y, field.X))
            {
                case JellystoneTable.JellystoneField.Basket:
                    field.Value = "B";
                    break;
                case JellystoneTable.JellystoneField.Blank:
                    field.Value = "";
                    break;
                case JellystoneTable.JellystoneField.RangerHL:
                case JellystoneTable.JellystoneField.RangerHR:
                case JellystoneTable.JellystoneField.RangerVD:
                case JellystoneTable.JellystoneField.RangerVU:
                    field.Value = "R";
                    break;
                case JellystoneTable.JellystoneField.Tree:
                    field.Value = "T";
                    break;
                case JellystoneTable.JellystoneField.Yogi:
                    field.Value = "Y";
                    break;
            }
        }

        #endregion
    }
}
